/*
*Copyright (c) Nikhil Jain.
*Project ICARUS, communications and 
*artificial intelligence.
*/



/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
 */
package ai.rrt;

//~--- JDK imports ------------------------------------------------------------

import java.util.Iterator;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 *
 * @author Nikzzay
 */
public class Tree {
    private ConcurrentHashMap<Integer, Tree> children;
    private ConcurrentHashMap<Node, Tree>    index;
    Tree                                     parent;
    final Node                               value;

    public Tree(Node value) {
        this.value               = value;
        this.parent              = null;
        index                    = new ConcurrentHashMap<>();
        children                 = new ConcurrentHashMap<>();
        value.pathLengthFromRoot = 0;
    }

    private Tree(Node value, Tree parent, ConcurrentHashMap<Node, Tree> index) {
        this.value  = value;
        this.parent = parent;
        this.index  = index;
        children    = new ConcurrentHashMap<>();
        index.put(value, this);
        updateValue();
    }

    public Tree add(Node value) {
        Tree child = new Tree(value, this, index);

        return children.put(value.hashCode(), child);
    }

    public Tree remove(Node value) {
        Tree child = children.remove(value.hashCode());

        child.setParent(null);

        return child;
    }

    public void changeParent(Node N) {
        this.parent.remove(this);

        Tree parent = index.get(N);

        parent.add(this);
        updateValue();
    }

    public void changeParent(Tree parent) {
        this.parent.remove(this);
        parent.add(this);
        updateValue();
    }

    public Tree getChild(Node N) {
        return children.get(N.hashCode());
    }

    /*
     * public void setValue(Node value) {
     *   this.value.setCoordinates(value.coords);
     *   this.value.pathLengthFromGoal= value.pathLengthFromGoal;
     *   this.value.pathLengthFromRoot= value.pathLengthFromRoot;
     *   this.value.cost= value.cost;
     * }
     */
    public void updateValue() {
        if (parent == null) {
            value.pathLengthFromRoot = 0;
            value.cost               = 0;
        } else {
            value.pathLengthFromRoot = parent.value.pathLengthFromRoot
                                       + RapidlyExplorableRandomTree.distanceBetween(value, parent.value);
            value.cost = value.pathLengthFromRoot;
        }

        Iterator iterate = children.values().iterator();

        while (iterate.hasNext()) {
            ((Tree) iterate.next()).updateValue();
        }
    }

    public Node value() {
        return value;
    }

    public Tree parent() {
        return parent;
    }

    public Tree child(Node value) {
        return children.get(value.hashCode());
    }

    public int nodeCount() {
        return index.size();
    }

    public Iterator treeIterator() {
        return index.values().iterator();
    }
    
    public Iterator nodeIterator() {
        return index.keySet().iterator();
    }

    @Override
    public boolean equals(Object o) {
        if (!(o instanceof Tree)) {
            return false;
        }

        return ((Tree) o).value.equals(value);
    }

    @Override
    public int hashCode() {
        int hash = 5;

        hash = 47 * hash + Objects.hashCode(this.value);

        return hash;
    }

    private Tree remove(Tree child) {
        child.setParent(null);

        return children.remove(child.value.hashCode());
    }

    private void setParent(Tree parent) {
        this.parent = parent;

        if (parent == null) {
            index.remove(value);
        }
    }

    private void add(Tree child) {
        children.put(child.value.hashCode(), child);
        index.put(child.value, child);
        child.parent = this;
    }
}


//~ Formatted by Jindent --- http://www.jindent.com
